/*
 * Copyright (C) 2011-2012 Me and My Shadow
 *
 * This file is part of Me and My Shadow.
 *
 * Me and My Shadow is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Me and My Shadow is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Me and My Shadow.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <iostream>
#include <string>
#include <vector>
#include "config.h"
#include "FileManager.h"
#include "Functions.h"
#include "Settings.h"
#include <SDL.h>
#include <SDL_syswm.h>
#include <archive.h>
#include <archive_entry.h>
#include <stdio.h>

using namespace std;

#ifdef WIN32
#include <windows.h>
#include <shlobj.h>
#include <shlwapi.h>
#include <shellapi.h>
#include <direct.h>
#pragma comment(lib,"shell32.lib")
#pragma comment(lib,"shlwapi.lib")
#else
#include <strings.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <dirent.h>
#endif

//Under Windows there's just one userpath.
#ifdef WIN32
string userPath,dataPath,appPath,exeName;
#define TO_UTF8(SRC, DEST) WideCharToMultiByte(CP_UTF8, 0, SRC, -1, DEST, sizeof(DEST), NULL, NULL)
#define TO_UTF16(SRC, DEST) MultiByteToWideChar(CP_UTF8, 0, SRC, -1, DEST, sizeof(DEST)/sizeof(DEST[0]))
#else
//But on other platforms we make a difference between the userPath (config files) and the userDataPath (data files).
//Finally there's the path for cache data userCachePath.
string userPath,userDataPath,userCachePath,dataPath,appPath,exeName;
#endif

bool configurePaths() {
	//Get the appPath and the exeName.
	{
		char s[4096];
		int i,m;
		#ifdef WIN32
		wchar_t ws[4096];
		m = GetModuleFileNameW(NULL, ws, sizeof(ws) / sizeof(ws[0]));
		ws[m] = 0;
		TO_UTF8(ws, s);
		m = strlen(s);
		#elif defined(ANDROID)
		//FIXME: Oops. There are no any executable files in Android.
		strcpy(s,"./meandmyshadow");
		m=strlen(s);
		#else
		m=readlink("/proc/self/exe",s,sizeof(s));
		#endif
		s[m]=0;
		for(i=m-1;i>=0&&i<4096;i--){
			if(s[i]=='/'||s[i]=='\\'){
				s[i]=0;
				break;
			}
		}
		appPath=s;
		exeName=s+i+1;
	}
	
	//TODO: Check if the userpath is empty before setting userPath???
	//Check if the userPath is empty.
	if(getUserPath().empty()){
#ifdef WIN32
		//Get the userPath.
		char s[4096];
		wchar_t ws[4096];
		SHGetSpecialFolderPathW(NULL,ws,CSIDL_PERSONAL,1);
		TO_UTF8(ws, s);
		userPath=s;
		userPath+="\\My Games\\meandmyshadow\\";		
#elif defined(ANDROID)
		//FIXME: These paths are relative to SDL Android data path.
		userPath="./";
		userDataPath="./";
		userCachePath="./";
#else
		//Temp variable that is used to prevent NULL assignement.
		char* env;
		
		//First get the $XDG_CONFIG_HOME env var.
		env=getenv("XDG_CONFIG_HOME");
		//If it's null set userPath to $HOME/.config/.
		if(env!=NULL){
			userPath=env;
		}else{
			userPath=getenv("HOME");
			userPath+="/.config";
		}
		//And add meandmyshadow to it.
		userPath+="/meandmyshadow/";
		
		//Now get the $XDG_DATA_HOME env var.
		env=getenv("XDG_DATA_HOME");
		//If it's null set userDataPath to $HOME/.local/share.

		if(env!=NULL){
			userDataPath=env;
		}else{
			userDataPath=getenv("HOME");
			userDataPath+="/.local/share";
		}
		//And add meandmyshadow to it.
		userDataPath+="/meandmyshadow/";
	
		//Now get the $XDG_CACHE_HOME env var.
		env=getenv("XDG_CACHE_HOME");
		//If it's null set userCachePath to $HOME/.cache.
		if(env!=NULL){
			userCachePath=env;
		}else{
			userCachePath=getenv("HOME");
			userCachePath+="/.cache";
		}
		//And add meandmyshadow to it.
		userCachePath+="/meandmyshadow/";
		
		//Set env null.
		env=NULL;
#endif
		
		//Print the userPath.
		cout<<"User preferences will be fetched from: "<<userPath<<endl;
#ifndef WIN32
		//In case of a non-Windows computer show the user data path.
		cout<<"User data will be fetched from: "<<userDataPath<<endl;
#endif
	}

#ifdef WIN32
	//Create the userPath folder and other subfolders.
	createDirectory(userPath.c_str());
	createDirectory((userPath+"levels").c_str());
	createDirectory((userPath+"levelpacks").c_str());
	createDirectory((userPath+"themes").c_str());
	createDirectory((userPath+"music").c_str());
	//The progress folder and subfolders.
	createDirectory((userPath+"progress").c_str());
	createDirectory((userPath+"progress\\main").c_str());
	createDirectory((userPath+"progress\\addon").c_str());
	createDirectory((userPath+"progress\\custom").c_str());
	createDirectory((userPath+"tmp").c_str());
	createDirectory((userPath+"images").c_str());
	//The records folder for recordings.
	createDirectory((userPath+"records").c_str());
	createDirectory((userPath+"records\\autosave").c_str());
	//And the custom folder inside the userpath.
	createDirectory((userPath+"custom").c_str());
	createDirectory((userPath+"custom\\levels").c_str());
	createDirectory((userPath+"custom\\levelpacks").c_str());
#else
	//Create the userPath.
	createDirectory(userPath.c_str());
	createDirectory(userDataPath.c_str());
	createDirectory(userCachePath.c_str());
	//Also create other folders in the userpath.
	createDirectory((userDataPath+"/levels").c_str());
	createDirectory((userDataPath+"/levelpacks").c_str());
	createDirectory((userDataPath+"/themes").c_str());
	createDirectory((userDataPath+"/music").c_str());
	//The progress folder and subfolders.
	createDirectory((userDataPath+"/progress").c_str());
	createDirectory((userDataPath+"/progress/main").c_str());
	createDirectory((userDataPath+"/progress/addon").c_str());
	createDirectory((userDataPath+"/progress/custom").c_str());
	createDirectory((userCachePath+"/tmp").c_str());
	createDirectory((userCachePath+"/images").c_str());
	//The records folder for recordings.
	createDirectory((userDataPath+"/records").c_str());
	createDirectory((userDataPath+"/records/autosave").c_str());
	//And the custom folder inside the userpath.
	createDirectory((userDataPath+"/custom").c_str());
	createDirectory((userDataPath+"/custom/levels").c_str());
	createDirectory((userDataPath+"/custom/levelpacks").c_str());
#endif

	//Get the dataPath by trying multiple relative locations.
	{
		FILE *f;
		string s;
		while(true){
			//try existing one
			if(!dataPath.empty()){
				s=dataPath+"font/knewave.ttf";
				if((f=fopen(s.c_str(),"rb"))!=NULL){
					fclose(f);
					break;
				}
			}
			//try "./"
			dataPath="./data/";
			s=dataPath+"font/knewave.ttf";
			if((f=fopen(s.c_str(),"rb"))!=NULL){
				fclose(f);
				break;
			}
			//try "../"
			dataPath="../data/";
			s=dataPath+"font/knewave.ttf";
			if((f=fopen(s.c_str(),"rb"))!=NULL){
				fclose(f);
				break;
			}
			//try App.Path
			dataPath=getAppPath()+"/data/";
			s=dataPath+"font/knewave.ttf";
			if((f=fopen(s.c_str(),"rb"))!=NULL){
				fclose(f);
				break;
			}
			//try App.Path+"/../"
			dataPath=getAppPath()+"/../data/";
			s=dataPath+"font/knewave.ttf";
			if((f=fopen(s.c_str(),"rb"))!=NULL){
				fclose(f);
				break;
			}
			//try DATA_PATH
#ifdef DATA_PATH
			dataPath=DATA_PATH;
			std::cout << "trying: " << dataPath << std::endl;
			s=dataPath+"font/knewave.ttf";
			if((f=fopen(s.c_str(),"rb"))!=NULL){
				fclose(f);
				break;
			}
#endif
#ifdef __APPLE__
            // TODO:
            /*extern std::string get_data_path();
            dataPath = get_data_path();
			dataPath=get_data_path();
			s=dataPath+"font/knewave.ttf";
			if((f=fopen(s.c_str(),"rb"))!=NULL){
				fclose(f);
				break;
			}
            */
#endif
			SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error! Faild to find game data!",
				"The game data files could not be found!", NULL);
			//error: can't find file
			return false;
		}
		//Print the dataPath.
		cout<<"Data files will be fetched from: "<<dataPath<<endl;
	}
	return true;
}

std::vector<std::string> enumAllFiles(std::string path,const char* extension,bool containsPath){
	vector<string> v;
#ifdef WIN32
	string s1;
	WIN32_FIND_DATAW f;
	if(!path.empty()){
		char c=path[path.size()-1];
		if(c!='/'&&c!='\\') path+="\\";
	}
	s1=path;
	if(extension!=NULL && *extension){
		s1+="*.";
		s1+=extension;
	}else{
		s1+="*";
	}
	wchar_t ws[4096];
	TO_UTF16(s1.c_str(), ws);
	HANDLE h=FindFirstFileW(ws,&f);
	if(h==NULL||h==INVALID_HANDLE_VALUE) return v;
	do{
		if(!(f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)){
			char s[1024];
			TO_UTF8(f.cFileName, s);
			if(containsPath){
				v.push_back(path+s);
			}else{
				v.push_back(s);
			}
		}
	}while(FindNextFileW(h,&f));
	FindClose(h);
	return v;
#else
	int len=0;
	if(extension!=NULL && *extension) len=strlen(extension);
	if(!path.empty()){
		char c=path[path.size()-1];
		if(c!='/'&&c!='\\') path+="/";
	}
	DIR *pDir;
	struct dirent *pDirent;
	pDir=opendir(path.c_str());
	if(pDir==NULL) return v;
	while((pDirent=readdir(pDir))!=NULL){
		if(pDirent->d_name[0]=='.'){
			if(pDirent->d_name[1]==0||
				(pDirent->d_name[1]=='.'&&pDirent->d_name[2]==0)) continue;
		}
		string s1=path+pDirent->d_name;
		struct stat S_stat;
		lstat(s1.c_str(),&S_stat);
		if(!S_ISDIR(S_stat.st_mode)){
			if(len>0){
				if((int)s1.size()<len+1) continue;
				if(s1[s1.size()-len-1]!='.') continue;
				if(strcasecmp(&s1[s1.size()-len],extension)) continue;
			}

			if(containsPath){
				v.push_back(s1);
			}else{
				v.push_back(string(pDirent->d_name));
			}
		}
	}
	closedir(pDir);
	return v;
#endif
}

std::vector<std::string> enumAllDirs(std::string path,bool containsPath){
	vector<string> v;
#ifdef WIN32
	string s1;
	WIN32_FIND_DATAW f;
	if(!path.empty()){
		char c=path[path.size()-1];
		if(c!='/'&&c!='\\') path+="\\";
	}
	s1=path+"*";
	wchar_t ws[4096];
	TO_UTF16(s1.c_str(), ws);
	HANDLE h = FindFirstFileW(ws, &f);
	if(h==NULL||h==INVALID_HANDLE_VALUE) return v;
	do{
		// skip '.' and '..' and hidden folders
		if(f.cFileName[0]=='.'){
			/*if(f.cFileName[1]==0||
				(f.cFileName[1]=='.'&&f.cFileName[2]==0))*/ continue;
		}
		if(f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY){
			char s[1024];
			TO_UTF8(f.cFileName, s);
			if (containsPath){
				v.push_back(path+s);
			}else{
				v.push_back(s);
			}
		}
	}while(FindNextFileW(h,&f));
	FindClose(h);
	return v;
#else
	if(!path.empty()){
		char c=path[path.size()-1];
		if(c!='/'&&c!='\\') path+="/";
	}
	DIR *pDir;
	struct dirent *pDirent;
	pDir=opendir(path.c_str());
	if(pDir==NULL) return v;
	while((pDirent=readdir(pDir))!=NULL){
		if(pDirent->d_name[0]=='.'){
			if(pDirent->d_name[1]==0||
				(pDirent->d_name[1]=='.'&&pDirent->d_name[2]==0)) continue;
		}
		string s1=path+pDirent->d_name;
		struct stat S_stat;
		lstat(s1.c_str(),&S_stat);
		if(S_ISDIR(S_stat.st_mode)){
			//Skip hidden folders.
			s1=string(pDirent->d_name);
			if(s1.find('.')==0) continue;
			
			//Add result to vector.
			if(containsPath){
				v.push_back(path+pDirent->d_name);
			}else{
				v.push_back(s1);
			}
		}
	}
	closedir(pDir);
	return v;
#endif
}

std::string processFileName(const std::string& s){
	string prefix=dataPath;
  
	//FIXME: Do we still need those last three?
	//REMARK: maybe 'return prefix+s;' is not needed (?)
	// it causes some bugs such as can't save level progress
	if(s.compare(0,6,"%DATA%")==0){
		if(s.size()>6 && (s[6]=='/' || s[6]=='\\')){
			return dataPath+s.substr(7);
		}else{
			return dataPath+s.substr(6);
		}
	}else if(s.compare(0,6,"%USER%")==0){
		if(s.size()>6 && (s[6]=='/' || s[6]=='\\')){
			return getUserPath(USER_DATA)+s.substr(7);
		}else{
			return getUserPath(USER_DATA)+s.substr(6);
		}
	}else if(s.compare(0,9,"%LVLPACK%")==0){
		if(s.size()>9 && (s[9]=='/' || s[9]=='\\')){
			return prefix+"levelpacks/"+s.substr(10);
		}else{
			return prefix+"levelpacks/"+s.substr(9);
		}
	}else if(s.compare(0,5,"%LVL%")==0){
		if(s.size()>5 && (s[5]=='/' || s[5]=='\\')){
			return prefix+"levels/"+s.substr(6);
		}else{
			return prefix+"levels/"+s.substr(5);
		}
	}else if(s.compare(0,8,"%THEMES%")==0){
		if(s.size()>8 && (s[8]=='/' || s[8]=='\\')){
			return prefix+"themes/"+s.substr(9);
		}else{
			return prefix+"themes/"+s.substr(8);
		}
	}else if(s.size()>0 && (s[0]=='/' || s[0]=='\\')){
		return s;
#ifdef WIN32
	// Another fix for Windows :(
	}else if(s.size()>1 && (s[1]==':')){
		return s;
#endif
	}else{
#if defined(ANDROID)
		//REMARK: maybe 'return prefix+s;' is not needed (?)
		// it causes some bugs such as can't save level progress in Android.
		return s;
#else
		return prefix+s;
#endif
	}
}

std::string compressFileName(const std::string& s){
	//FIXME: Do we need the other ones from processFileName?
	
	//Check if the data path is at the start.
	size_t pos=s.find(getDataPath());
	if(pos!=string::npos){
		pos+=getDataPath().size();
		return "%DATA%/"+s.substr(pos);
	}
	pos=s.find(getUserPath(USER_DATA));
	if(pos!=string::npos){
		pos+=getUserPath(USER_DATA).size();
		return "%USER%/"+s.substr(pos);
	}

	return s;
}

std::string fileNameFromPath(const std::string &path, const bool webURL){
	std::string filename;
	size_t pos;
#ifdef WIN32
	// NOTE: '/' in string should be '/' not '\/',
	// we don't need to escape it
	if(webURL){
		pos = path.find_last_of("/");
	}else{
		// NOTE: sometimes path separator in Windows can be '/',
		// so we must check botn '\' and '/'
		pos = path.find_last_of("\\/");
	}
#else
	// NOTE: '/' in string should be '/' not '\/',
	// we don't need to escape it
	pos = path.find_last_of("/");
#endif
	if(pos != std::string::npos)
		filename.assign(path.begin() + pos + 1, path.end());
	else
		filename=path;
	
	return filename;
}

std::string pathFromFileName(const std::string &filename){
	std::string path;
	// NOTE: '/' in string should be '/' not '\/',
	// we don't need to escape it
#ifdef WIN32
	// NOTE: sometimes path separator in Windows can be '/',
	// so we must check botn '\' and '/'
	size_t pos = filename.find_last_of("\\/");
#else
	size_t pos = filename.find_last_of("/");
#endif
	if(pos != std::string::npos)
		path.assign(filename.begin(), filename.begin() + pos +1);
	else
		path=filename;
	
	return path;
}

bool extractFile(const string &fileName, const string &destination) {
	//Create the archive we're going to extract.
	archive* file;
	//Create the destination we're going to extract to.
	archive* dest;
	
	file=archive_read_new();
	dest=archive_write_disk_new();
	archive_write_disk_set_options(dest, ARCHIVE_EXTRACT_TIME);
	
	archive_read_support_format_zip(file);
	
	//Now read the archive.
    if(archive_read_open_filename(file,fileName.c_str(),10240)) {
		cerr<<"ERROR: An error occurred while reading archive "+fileName<<endl;
		return false;
	}
	
	//Now write every entry to disk.
	int status;
	archive_entry* entry=NULL;
	while(true) {
		status=archive_read_next_header(file,&entry);
		if(status==ARCHIVE_EOF){
			break;
		}
		if(status!=ARCHIVE_OK){
			cerr<<"ERROR: An error occurred while reading archive "+fileName<<endl;
			return false;
		}
		archive_entry_copy_pathname(entry,(destination+archive_entry_pathname(entry)).c_str());
		
		status=archive_write_header(dest,entry);
		if(status!=ARCHIVE_OK){
			cerr<<"ERROR: An error occurred while extracting archive "+fileName<<endl;
			return false;
		}else{
			copyData(file, dest);
			status=archive_write_finish_entry(dest);
			if(status!=ARCHIVE_OK){
				cerr<<"ERROR: An error occurred while extracting archive "+fileName<<endl;
				return false;
			}

		}
	}
	
	//Finally close the archive.
	archive_read_close(file);
    archive_read_free(file);
	return true;
}

bool dirExists(const char* dir){
#if defined(WIN32)
	wchar_t ws[4096];
	TO_UTF16(dir, ws);
	DWORD attr=GetFileAttributesW(ws);
	if(attr==INVALID_FILE_ATTRIBUTES) return false;
	return (attr & FILE_ATTRIBUTE_DIRECTORY)!=0;
#else
	struct stat sb;
	if(stat(dir,&sb) == 0 && S_ISDIR(sb.st_mode)){
		return true;
	}
	return false;
#endif
}

bool createDirectory(const char* path){
#ifdef WIN32
	wchar_t s0[4096],s1[4096],s[4096];

	GetCurrentDirectoryW(sizeof(s0)/sizeof(s0[0]),s0);
	for (unsigned int i = 0; i < sizeof(s0) / sizeof(s0[0]); i++){
		if (s0[i] == '\0') break;
		else if (s0[i] == '/') s0[i] = '\\';
	}

	TO_UTF16(path, s1);
	for (unsigned int i = 0; i < sizeof(s1) / sizeof(s1[0]); i++){
		if (s1[i] == '\0') break;
		else if (s1[i] == '/') s1[i] = '\\';
	}

	PathCombineW(s,s0,s1);

	int ret = SHCreateDirectoryExW(NULL, s, NULL);
	switch(ret) {
	case ERROR_SUCCESS:
	case ERROR_FILE_EXISTS:
	case ERROR_ALREADY_EXISTS:
		return true;
	default:
		return false;
	}
#else
	return mkdir(path,0777)==0;
#endif
}

bool removeDirectory(const char *path){
#ifdef WIN32
	SDL_SysWMinfo info = {};
	SDL_VERSION(&info.version);
	SDL_GetWindowWMInfo(sdlWindow, &info);

	wchar_t s0[4096], s1[4096], s[4096];

	GetCurrentDirectoryW(sizeof(s0) / sizeof(s0[0]), s0);
	TO_UTF16(path, s1);
	PathCombineW(s, s0, s1);

	for (unsigned int i = 0; i<sizeof(s) / sizeof(s[0]); i++){
		if (s[i] == '\0') {
			s[i + 1] = '\0';
			break;
		}
		else if (s[i] == '/') s[i] = '\\';
	}

	SHFILEOPSTRUCTW op = {
		info.info.win.window,
		FO_DELETE,
		s,
		NULL,
		FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT,
		FALSE,
		0,
		NULL,
	};
	int ret = SHFileOperationW(&op);
	return ret == 0 && op.fAnyOperationsAborted == FALSE;
#else
	//Open the directory that needs to be removed.
	DIR* d=opendir(path);
	//Get the path length
	size_t path_len = strlen(path);
	//Boolean if the directory is emptied.
	//True: succes		False: failure
	//Default is true because if the directory is empty it will never enter the while loop, but we still have success.
	bool r = true;

	//Check if the directory exists.
	if(d) {
		//Pointer to an entry of the directory.
		struct dirent* p;

		//Loop the entries of the directory that needs to be removed as long as there's no error.
		while(r && (p=readdir(d))) {

			/* Skip the names "." and ".." as we don't want to recurse on them. */
			if (!strcmp(p->d_name, ".") || !strcmp(p->d_name, "..")) {
				//The filename is . or .. so we continue to the next entry.
				continue;
			} else {
				//r2 tells if the entry is deleted.
				//True: succes		False: failure
				//Default is false.
				bool r2 = false;
				char* buf;
				size_t len;

				//Get the length of the path + the directory entry name.
				len = path_len + strlen(p->d_name) + 2; 
				buf = (char*) malloc(len);

				if(buf) {
					struct stat statbuf;
					snprintf(buf, len, "%s/%s", path, p->d_name);

					if(!stat(buf, &statbuf)){
						//Check if the entry is a directory or a file.
						if (S_ISDIR(statbuf.st_mode)){
							//We call ourself(removeDirectory) recursively.
							//We return true on success.
							r2 = removeDirectory(buf);
						}else{
							//unlink() returns zero on succes so we set r2 to the unlink(buf)==0.
							r2 = unlink(buf)==0;
						}
					}
					//Free the buf.
					free(buf);
				}
				//We set r to r2 since r2 contains the status of the latest deletion.
				r = r2;
			}
		}
		//Close the directory.
		closedir(d);
	}
	
	//The while loop has ended, meaning we (tried) cleared the directory.
	//If r is true, meaning no errors we can delete the directory.
	if(r){
		//The return value of rmdir is 0 when it succeeded.
		r = rmdir(path)==0;
	}
	
	//Return the status.
	return r;
#endif
}

bool renameDirectory(const char* oldPath,const char* newPath){
	return rename(oldPath,newPath)==0;
}


void copyData(archive* file, archive* dest) {
	int status;
	const void* buff;
	size_t size;
#if ARCHIVE_VERSION_NUMBER < 3000000
	off_t offset;
#else
	int64_t offset;
#endif


	while(true) {
		status=archive_read_data_block(file, &buff, &size, &offset);
		if(status==ARCHIVE_EOF){
			return;
		}
		if(status!=ARCHIVE_OK){
			cerr<<"ERROR: An error occurred while writing data to disk."<<endl;
			return;
		}
		status=archive_write_data_block(dest, buff, size, offset);
		if(status!=ARCHIVE_OK) {
			cerr<<"ERROR: An error occurred while writing data to disk."<<endl;
			return;
		}
	}
}

bool copyFile(const char* source,const char* dest){
	//Open the source file.
	ifstream fin(source,fstream::binary);
	if(!fin)
		return false;
	
	//Open the dest file.
	ofstream fout(dest,fstream::trunc|fstream::binary);
	if(!fout)
		return false;
	
	//Copy.
	fout<<fin.rdbuf();
	return true;
}

bool removeFile(const char* file){
	return remove(file)==0;
}

bool fileExists(const char* file){
#ifdef WIN32
	wchar_t ws[4096];
	TO_UTF16(file, ws);
	bool ret=false;
	HANDLE h=CreateFileW(ws,0,FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,NULL,OPEN_EXISTING,0,NULL);
	if(h!=INVALID_HANDLE_VALUE){
		ret=true;
		CloseHandle(h);
	}
	return ret;
#else
	return (access(file,F_OK)==0);
#endif
}

bool createFile(const char* file){
	//Open the file with write permission.
	FILE* f=fopen(file,"wb");
	
	//Check if there are no problems.
	if(f){
		//Close the file.
		fclose(f);
		return true;
	}else{
		return false;
	}
}

//Escape invalid characters in a file name (mainly for Windows).
std::string escapeFileName(const std::string& fileName) {
	std::string ret;

	for (int i = 0, m = fileName.size(); i < m; i++) {
		bool escape = false;
		char c = fileName[i];

		switch (c) {
		case '\"': case '*': case '/': case ':': case '<':
		case '>': case '?': case '\\': case '|': case '%':
			escape = true;
			break;
		}
		if (c <= 0x1F || c >= 0x7F) escape = true;
		if (i == 0 || i == m - 1) {
			switch (c) {
			case ' ': case '.':
				escape = true;
				break;
			}
		}

		if (escape) {
			char s[16];
			sprintf(s, "%%%02X", (int)(unsigned char)c);
			ret += s;
		} else {
			ret.push_back(c);
		}
	}

	return ret;
}
